D:\git\skunkworks\herald-for-cpp\herald\include\herald\engine\coordinator.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2021 Herald Project Contributors |
2 | | // SPDX-License-Identifier: Apache-2.0 |
3 | | // |
4 | | |
5 | | #ifndef HERALD_COORDINATOR_H |
6 | | #define HERALD_COORDINATOR_H |
7 | | |
8 | | #include "../context.h" |
9 | | #include "activities.h" |
10 | | #include "../data/sensor_logger.h" |
11 | | |
12 | | #include <map> |
13 | | #include <vector> |
14 | | #include <algorithm> |
15 | | #include <iterator> |
16 | | #include <optional> |
17 | | |
18 | | namespace herald { |
19 | | |
20 | | /// \brief Engine classes provide for task scheduling, including complex inter-dependent tasks. |
21 | | namespace engine { |
22 | | |
23 | | /// |
24 | | /// \brief Coordinates all connection and activities used across all sensors within Herald |
25 | | /// |
26 | | /// Responsible for:- |
27 | | /// |
28 | | /// - Determining Sensor capabilities and requirements around connections and Activity instances |
29 | | /// |
30 | | /// Manages:- |
31 | | /// |
32 | | /// - Nothing, but coordinates activities throughout Herald Sensor networks on behalf of SensorArray |
33 | | /// |
34 | | /// Is managed by:- |
35 | | /// |
36 | | /// - SensorArray |
37 | | /// |
38 | | template <typename ContextT> |
39 | | class Coordinator { |
40 | | public: |
41 | | /// Default constructor. Receives a configured platform-specific context instance. |
42 | | Coordinator(ContextT& ctx) |
43 | | : context(ctx), |
44 | | providers(), |
45 | | running(false) |
46 | | HLOGGERINIT(ctx,"engine","coordinator") |
47 | 1 | {} |
48 | | |
49 | 1 | ~Coordinator() = default; |
50 | | |
51 | | /// Introspect and include in iteration planning |
52 | | template <typename SensorT> |
53 | 1 | void add(SensorT& sensor) { |
54 | 1 | HTDBG("Adding sensor"); |
55 | 1 | auto prov = sensor.coordinationProvider(); |
56 | 1 | if (prov.has_value()) { |
57 | 1 | HTDBG("Sensor has Provider implementation"); |
58 | 1 | providers.push_back(prov.value()); |
59 | 1 | } |
60 | 1 | } |
61 | | /// Remove from iteration planning |
62 | | template <typename SensorT> |
63 | | void remove(SensorT& sensor) |
64 | | { |
65 | | // TODO support remove |
66 | | } |
67 | | |
68 | | /// Prepares for iterations to be called (may pre-emptively make calls) |
69 | 1 | void start() { |
70 | 1 | HTDBG("Start called"); |
71 | 1 | // Clear feature providers |
72 | 1 | featureProviders.clear(); |
73 | 1 | // Fetch feature providers |
74 | 1 | for (auto prov: providers) { |
75 | 1 | auto myFeatures = prov.get().connectionsProvided(); |
76 | 1 | for (auto feature : myFeatures) { |
77 | 1 | featureProviders.emplace(feature,prov); |
78 | 1 | } |
79 | 1 | } |
80 | 1 | running = true; |
81 | 1 | HTDBG("Start returning"); |
82 | 1 | } |
83 | | |
84 | | /// Execute an iteration of activity, according to settings |
85 | 4 | void iteration() { |
86 | 4 | if (!running) { |
87 | 0 | HTDBG("Coordinator not running. Returning from iteration having done nothing."); |
88 | 0 | return; |
89 | 0 | } |
90 | 4 | HTDBG("################# ITERATION #################"); |
91 | 4 | // HTDBG("Entered iteration"); |
92 | 4 | // Create empty list of required prereqs per provider |
93 | 4 | std::map<std::reference_wrapper<CoordinationProvider>,std::vector<PrioritisedPrerequisite>> assignPrereqs; |
94 | 4 | for (auto& prov : providers) { |
95 | 4 | assignPrereqs.emplace(prov,std::vector<PrioritisedPrerequisite>()); |
96 | 4 | } |
97 | 4 | // HTDBG("Completed initialisation of provider prerequisities containers"); |
98 | 4 | // HTDBG(" - Provider count: {}", providers.size()); |
99 | 4 | |
100 | 4 | std::vector<PrioritisedPrerequisite> connsRequired; |
101 | 4 | // Loop over providers and ask for feature pre-requisites |
102 | 4 | for (auto& prov : providers) { |
103 | 4 | auto myConns = prov.get().requiredConnections(); |
104 | 4 | std::copy(myConns.begin(),myConns.end(), |
105 | 4 | std::back_insert_iterator<std::vector<PrioritisedPrerequisite>>(connsRequired)); |
106 | 4 | } |
107 | 4 | // HTDBG(std::to_string(connsRequired.size())); |
108 | 4 | // HTDBG("Retrieved providers' current prerequisites"); |
109 | 4 | // TODO de-duplicate pre-reqs |
110 | 4 | // Now link required prereqs to each provider |
111 | 4 | for (auto& p : connsRequired) { |
112 | 2 | auto el = featureProviders.find(std::get<0>(p)); // find provider for given prereq by feature tag |
113 | 2 | if (featureProviders.end() != el) { |
114 | 2 | assignPrereqs[el->second].push_back(p); |
115 | 2 | } |
116 | 2 | } |
117 | 4 | // HTDBG("Linked pre-reqs to their providers"); |
118 | 4 | |
119 | 4 | // // Some debug checks here |
120 | 4 | // int cnt = 0; |
121 | 4 | // for (auto& ass : assignPrereqs) { |
122 | 4 | // // HTDBG("assign prereqs number {} has this many prereqs to fill {}", cnt, ass.second.size()); |
123 | 4 | // cnt++; |
124 | 4 | // } |
125 | 4 | |
126 | 4 | // Communicate with relevant feature providers and request features for targets (in descending priority order) |
127 | 4 | // - Includes removal of previous features no longer needed |
128 | 4 | std::vector<PrioritisedPrerequisite> provisioned; |
129 | 4 | for (auto& prov : assignPrereqs) { |
130 | 4 | // TODO sort by descending priority before passing on |
131 | 4 | |
132 | 4 | // FOR PLATFORMS WITH STD::FUTURE AND STD::ASYNC |
133 | 4 | // std::future<void> fut = std::async(std::launch::async, |
134 | 4 | // &CoordinationProvider::provision, prov.first, |
135 | 4 | // //prov.first->provision( |
136 | 4 | // prov.second,[&provisioned] ( |
137 | 4 | // const std::vector<PrioritisedPrerequisite> myProvisioned) -> void { |
138 | 4 | // std::copy(myProvisioned.begin(),myProvisioned.end(), |
139 | 4 | // std::back_insert_iterator<std::vector<PrioritisedPrerequisite>>(provisioned)); |
140 | 4 | // }); |
141 | 4 | // fut.get(); // waits for callback // TODO wait with timeout |
142 | 4 | |
143 | 4 | // FOR OTHER PLATFORMS (E.g. ZEPHYR):- |
144 | 4 | std::vector<PrioritisedPrerequisite> myProvisioned = prov.first.get().provision(prov.second); |
145 | 4 | std::copy(myProvisioned.begin(),myProvisioned.end(), |
146 | 4 | std::back_insert_iterator<std::vector<PrioritisedPrerequisite>>(provisioned)); |
147 | 4 | } |
148 | 4 | // HTDBG("All pre-requisities requests sent and responses received"); |
149 | 4 | // TODO do the above asynchronously and await callback or timeout for all |
150 | 4 | |
151 | 4 | // For each which are now present, ask for activities (in descending priority order) |
152 | 4 | for (auto& prov : providers) { |
153 | 4 | auto maxActs = prov.get().requiredActivities(); |
154 | 4 | // TODO sort by descending priority before actioning |
155 | 4 | for (auto& act : maxActs) { |
156 | 2 | std::string san("Activity "); |
157 | 2 | san += act.name; |
158 | 2 | HTDBG(san); |
159 | 2 | // HTDBG("Checking next desired activity for prereqs being satisfied"); |
160 | 2 | // Filter requested by provisioned |
161 | 2 | bool allFound = true; |
162 | 2 | for (auto& pre : act.prerequisites) { |
163 | 2 | bool myFound = false; |
164 | 2 | for (auto& exists : provisioned) { |
165 | 2 | if (std::get<0>(pre) == std::get<0>(exists) && |
166 | 2 | std::get<1>(pre) == std::get<2>(exists)) { |
167 | 2 | myFound = true; |
168 | 2 | } |
169 | 2 | } |
170 | 2 | allFound = allFound & myFound; |
171 | 2 | if (myFound) { |
172 | 2 | HTDBG(" - Prereq satisfied"); |
173 | 2 | } else { |
174 | 0 | HTDBG(" - Prereq NOT SATISFIED"); |
175 | 0 | } |
176 | 2 | } |
177 | 2 | // Carry out activities with completion callbacks passed in |
178 | 2 | if (allFound) { |
179 | 2 | HTDBG("All satisfied, calling activity"); |
180 | 2 | // do activity |
181 | 2 | |
182 | 2 | // FOR PLATFORMS WITH STD::ASYNC |
183 | 2 | // act.executor(act,[this] (Activity act, std::optional<Activity> followOn) -> void { |
184 | 2 | // // TODO handle result |
185 | 2 | // // TODO Carry out any follow up activities |
186 | 2 | // HTDBG("Activity completion callback called"); |
187 | 2 | // }); |
188 | 2 | |
189 | 2 | // FOR PLATFORMS WITHOUT |
190 | 2 | std::optional<Activity> followOn = act.executor(act); |
191 | 2 | // TODO carry out follow on activity until no more follow ons (or max follow on number hit) |
192 | 2 | } |
193 | 2 | } |
194 | 4 | } |
195 | 4 | // HTDBG("Leaving iteration"); |
196 | 4 | HTDBG("################# END #################"); |
197 | 4 | } |
198 | | /// Closes out any existing connections/activities |
199 | 1 | void stop() { |
200 | 1 | running = false; |
201 | 1 | } |
202 | | |
203 | | private: |
204 | | ContextT& context; |
205 | | |
206 | | std::vector<std::reference_wrapper<CoordinationProvider>> providers; |
207 | | std::map<FeatureTag,std::reference_wrapper<CoordinationProvider>> featureProviders; |
208 | | |
209 | | bool running; |
210 | | |
211 | | HLOGGER(ContextT); |
212 | | }; |
213 | | |
214 | | // /** Comparator for less than (use in maps) **/ |
215 | | bool operator<(const std::reference_wrapper<CoordinationProvider> first, const std::reference_wrapper<CoordinationProvider> second); |
216 | | |
217 | | } |
218 | | } |
219 | | |
220 | | #endif |